#include "PrecompiledHeader_Reborn.h"
#include "CExplorer.h"

/*--------------------------------------------------------------------------*/
CExplorer* g_Explorer = NULL;
/*--------------------------------------------------------------------------*/

#define EXPDEBUG

CExplorer::CExplorer() :
	IGameMode("CExplorer"),
	IRender(0)
{
	g_Shell->RegisterLuaFunction("explorer", sh::explorer);
	g_Shell->RegisterLuaFunction("navigateto", sh::navigateto);

	this->m_navigation = false;

	this->m_pendingPushData = false;
	this->m_pendingPopData = false;

	this->StartTask(0, CalcDesc(true, ECalc::SingleThreadHighPriority));

	RenderTaskDesc desc;
	desc.m_material = NULL;
	desc.m_argument = 0;
	desc.m_flags = ERenderTaskFlag::UserInterface | ERenderTaskFlag::NoMaterial;
	desc.m_priority = 98;
	desc.m_object = this;

	this->AddRenderTask(desc);

	/*--------------------------------------------------------------------------*/

	GLTextureDesc td;
	td.internalFormat = GL_RGBA8;
	td.height = 0;
	td.width = 0;
	td.data = NULL;
	td.format = GL_RGBA;
	td.type = GL_UNSIGNED_BYTE;

	for (uint i = 0; i < CEXPLORER_MAX_DEPTH; ++i)
	{
		std::stringstream ss;
		ss << "Explorer#" << i;

		td.name = ss.str();
		this->m_outputTextures[i] = ROpenGL::GenTexture(&td);
	}

	this->PushCelestial(NULL, ECelestialType::Galaxyfield, NULL, glm::vec3(0.0f, 0.0f, 0.0f));
//
// 	StarSystemDesc sd;
// 	sd.m_color = Color::Random();
// 	sd.m_id = 10;
// 	sd.m_position = glm::vec3(0, 0, 110);
// 	sd.m_strength = 1000.0f;
//
// 	this->Push(this->m_celestialsStack.back().m_owner, ECelestialType::Starfield, &sd, glm::normalize(glm::vec3(1.0f, 0.0f, 0.0f)));
}

/*--------------------------------------------------------------------------*/

CExplorer::~CExplorer()
{

}

/*--------------------------------------------------------------------------*/

void CExplorer::InitGameMode()
{

}

/*--------------------------------------------------------------------------*/

void CExplorer::OnCalc(uint arg)
{
	this->BindRenderTasks(g_Renderer->GetUIRenderPhase());

	// bo to fruwa na singlethreadzie
	if (this->m_pendingPushData)
	{
		this->m_pendingPushData = false;
		this->PushCelestial(this->m_src, this->m_type, this->m_initData, this->m_pendingPushVector);
	}

	if (this->m_pendingPopData)
	{
		this->m_pendingPopData = false;
		this->PopCelestial(this->m_pendingPopVector);
	}

	if (this->m_navigation)
	{
		ICelestial* back = this->m_celestialsStack.back().m_owner;
		PerspectiveCamera* camera = this->m_celestialsStack.back().m_camera;

		if (back)
		{
			glm::vec3 diff = (back->GetChildPosition(this->m_navId) - camera->GetPosition()) * 0.1f;
			camera->SetPosition(camera->GetPosition() + diff);
		}
	}

	for (auto iter = this->m_celestialsStack.begin(); iter != this->m_celestialsStack.end(); ++iter)
	{
		if (iter->m_owner)
		{
			iter->m_owner->CalculateChildsMovement();
		}
	}
}

/*--------------------------------------------------------------------------*/

void CExplorer::OnRender(const RenderTask* renderTask, uint camarg)
{
	std::stringstream ss;
	ss << "Location: ";

	PerspectiveCamera* camera = this->m_celestialsStack.back().m_camera;

	ROpenGL::ClearBuffers();

	for (auto iter = this->m_celestialsStack.begin(); iter != this->m_celestialsStack.end(); ++iter)
	{
		ss << " >> " << iter->m_owner->GetName();

		ROpenGL::BlendEnabled(GL_TRUE);
		ROpenGL::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		g_Renderer->DrawRectangle2D(glm::vec2(), g_Settings->GetResolutionf(), iter->m_output);
	}

	g_Renderer->DrawText2D(NULL, ss.str(), glm::vec2(10.0f, 10.0f), Color::White);
}

/*--------------------------------------------------------------------------*/

void CExplorer::PushCelestial(ICelestial* toClone, ECelestialType::Type type, void* initData, const glm::vec3& vector)
{
	this->m_navigation = false;

	CameraDesc cd;

	if (toClone)
	{
		toClone->OnLeave();

		cd = toClone->GetChildCameraDesc();
		cd.m_name = toClone->GetName() + "_CAMERA";
	}
	else
	{
		cd.m_farplane = 100000.0f;
		cd.m_nearplane = 0.01f;
		cd.m_fov = 70.0f;
		cd.m_name = "ROOT_OF_ROOTS_CAMERA";
		cd.m_method = ERenderPhaseMethod::NormalRendering;
	}

	cd.m_output = this->m_outputTextures[this->m_celestialsStack.size()];

	/*--------------------------------------------------------------------------*/

	std::string newCelestialName = toClone ? toClone->GetName() : "NoName";

	Explorer explorer;
	explorer.m_name = newCelestialName;
	explorer.m_scene = g_Scene->CreateScene();
	explorer.m_camera = explorer.m_scene->SetCamera(cd);
	explorer.m_type = EExplorerType::Star;
	explorer.m_output = cd.m_output;
	explorer.m_scene->StartScene(newCelestialName + "_SCENE", 0);

	NodeCreation nc;
	nc.m_flags = 0;
	nc.m_parent = explorer.m_scene->GetRootNode();
	nc.m_scene = explorer.m_scene;

	ICelestial* object = NULL;

	glm::vec3 cameraPosition;

	switch (type)
	{
	case ECelestialType::Galaxyfield:
	{
		object = new GalaxyField(nc);
		break;
	}

	case ECelestialType::Star:
	{
		object = new StarSystem(nc);
		break;
	}

	default:
	{
		AssertLogC(true, "Unknown ECelestialType");
	}
	}

	AssertLogC(object->CanBeHost() == false, "Celestial not designed to be a scene host!");

	object->ProcessInitData(initData);
	object->GenerateContent();
	object->OnEnter();

	explorer.m_owner = object;

	if (this->m_celestialsStack.size())
	{
		explorer.m_camera->SetQuaternion(this->m_celestialsStack.back().m_camera->GetQuaterion());
		explorer.m_camera->SetPosition(m_pendingPushVector * object->GetRadius() * 0.95f);

		this->m_celestialsStack.back().m_camera->BindQuaternionSource(explorer.m_camera);
		this->m_celestialsStack.back().m_camera->SetPosition(this->m_celestialsStack.back().m_camera->GetPosition() + m_pendingPushVector);
	}

	///explorer.m_camera->SetPosition(glm::vec3(10000.0f, 10000.0f, 10000.0f));
	g_Scene->SetFreeCamera(explorer.m_camera);
	this->m_celestialsStack.push_back(explorer);
}

/*--------------------------------------------------------------------------*/

void CExplorer::PopCelestial(const glm::vec3& vector)
{
	AssertLogC(this->m_celestialsStack.empty(), "Empty CelestialsStack!");
	Explorer removedExplorer = this->m_celestialsStack.back();
	this->m_celestialsStack.pop_back();

	removedExplorer.m_owner->OnLeave();
	removedExplorer.m_owner->DestroyContent();

	/*--------------------------------------------------------------------------*/

	AssertLogC(this->m_celestialsStack.empty(), "Empty CelestialsStack!");
	Explorer& currentExplorer = this->m_celestialsStack.back();

	currentExplorer.m_owner->OnEnter();
	currentExplorer.m_camera->UnbindQuaternionSource();

	g_Shell->GetVar("fcamera_movement_speed")->Set(1.0f);

	g_Scene->DeleteScene(removedExplorer.m_scene);
	g_Scene->SetFreeCamera(currentExplorer.m_camera);

	currentExplorer.m_camera->SetPosition(currentExplorer.m_camera->GetPosition() + (this->m_pendingPopVector * 2.0f));
}

/*--------------------------------------------------------------------------*/

void CExplorer::Push(ICelestial* src, ECelestialType::Type type, void* initData, const glm::vec3& vector)
{
	if (!this->m_pendingPushData)
	{
		this->m_src = src;
		this->m_type = type;
		this->m_initData = initData;
		this->m_pendingPushData = true;
		this->m_pendingPushVector = vector;
	}
}

/*--------------------------------------------------------------------------*/

void CExplorer::Pop(const glm::vec3& vector)
{
	if (!this->m_pendingPopData)
	{
		if (this->m_celestialsStack.size() > 1)
		{
			this->m_pendingPopData = true;
			this->m_pendingPopVector = vector;
		}
	}
}

/*--------------------------------------------------------------------------*/

void CExplorer::Log()
{
	for (auto iter = this->m_celestialsStack.begin(); iter != this->m_celestialsStack.end(); ++iter)
	{
		iter->m_owner->Log();
	}
}

/*--------------------------------------------------------------------------*/

void CExplorer::NavigateTo(uint i)
{
	this->m_navigation = true;
	this->m_navId = i;
}

/*--------------------------------------------------------------------------*/

void sh::explorer()
{
	g_Explorer->Log();
}

/*--------------------------------------------------------------------------*/

void sh::navigateto(uint i)
{
	g_Explorer->NavigateTo(i);
}

/*--------------------------------------------------------------------------*/